// VertexDirectory class implementation
#include "VertexDirectory.h"
#include "Vertex.h"
#include "Helpers.h"
#include <iostream>
#include <vector>
#include <map>
#include <stdlib.h>
#include <algorithm>

using namespace std;

map<VertexSetKey, VertexSet*> VertexDirectory::m_VertexSets;
//-----------------------------------------------------------------------------------------
// Public
//-----------------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------
// ctor
// creates a new instance of VertexDirectory containing all the vertices in the 
// provided map.
//-----------------------------------------------------------------------------------------
VertexDirectory::VertexDirectory(std::map<vector<bool>,Vertex*> *vertices)
{
	m_WorkingCopyVertices;
	try
	{
		m_WorkingCopyVertices = new map<vector<bool>, Vertex*>();
	}
	catch(std::bad_alloc *e) //memory allocation failed
	{
		e = NULL;
		Logger::LogMessage("ERROR: Unable to create the vertices map in VertexDirectory::constructor");
		throw new GraphColoringException("ERROR: Unable to create vertices in VertexDirectory::constructor");
	}
	map<vector<bool>, Vertex*>::iterator it;
	for(it = vertices->begin(); it != vertices->end(); it++)
	{
		Vertex* vertex;
		try
		{
			vertex = new Vertex((*it).second->Name(), this);
		}
		catch(std::bad_alloc *e) //memory allocation failed
		{
			e = NULL;
			Logger::LogMessage("ERROR: Unable to create a vertex in VertexDirectory::constructor");
			map<vector<bool>, Vertex*>::iterator cleanupIt;
			for(cleanupIt = vertices->begin(); cleanupIt != it; cleanupIt++)
			{
				delete it->second;
			}
			throw new GraphColoringException("ERROR: Unable to create a vertex in VertexDirectory::constructor");
		}
		vertex->Copy((*it).second);
		this->AddVertexToDirectory(vertex, m_WorkingCopyVertices);
	}
}

//-----------------------------------------------------------------------------------------
// dtor
//-----------------------------------------------------------------------------------------
VertexDirectory::~VertexDirectory()
{
	map<vector<bool>, Vertex*>::iterator it;
	// free all the vertices in the backup directory
	for (it = m_WorkingCopyVertices->begin(); it != m_WorkingCopyVertices->end(); it++)
	{
		free((*it).second);
	}
	// free the backup directory pointer
	m_WorkingCopyVertices->~map();
	free(m_WorkingCopyVertices);
}

//----------------------------------------------------------------------------------
// GetStaticCopyVertex
//returns a pointer to the vertex with the name "name" in vertices directory
//----------------------------------------------------------------------------------
Vertex* VertexDirectory::GetStaticCopyVertex(vector<bool> name)
{
	return GetStaticCopyVertexFromDirectory(name, m_Vertices);
}

//----------------------------------------------------------------------------------
// GetWorkingCopyVertex
//returns a pointer to the vertex with the name "name" in vertices directory
//----------------------------------------------------------------------------------
Vertex* VertexDirectory::GetWorkingCopyVertex(vector<bool> name)
{
	if (m_WorkingCopyVertices == NULL)
	{
		throw new GraphColoringException("no backup vertex directory specified for GetWorkingCopyVertex");
	}
	return GetStaticCopyVertexFromDirectory(name, m_WorkingCopyVertices);
}

//----------------------------------------------------------------------------------
// CreateAllVertices
// returns a set containing all the vertices that should be in the graph
// n = length of the vertex name, w = weight of the vertex name
//----------------------------------------------------------------------------------
set<vector<bool> >* VertexDirectory::CreateAllVertices(int n, int w)
{
	set<vector<bool> >* vertices;
	try
	{
		vertices = new set<vector<bool> >;
	}
	catch(std::bad_alloc *e) //memory allocation failed
	{
		e = NULL;
		Logger::LogMessage("memory allocation failed in VertexDirectory::CreateAllVertices. Vertex directory wasn't created.");
		return NULL;
	}
	
	bool *vertexName = new bool[n];
	memset(vertexName, 0, n*sizeof(bool));
	
	//change the name to the first vertex in the graph acording to the w given
	//create the first permutaion(the smalest - vertex name of the form: 000000..001111..11)
	for(int i = 0; i < w ; i++)
	{
		vertexName[n-1-i] = true;
	}

	vector<bool> name(vertexName, vertexName+n); //pointer aritmetics
	vector<bool>::iterator start = name.begin();
	vector<bool>::iterator end = name.end();

	set<vector<bool>>::iterator it = vertices->begin();

	bool isNextPerm = true;
	while (isNextPerm)
	{
		it = vertices->insert(it,name); //makes a copy of the given data
		isNextPerm = next_permutation(start, end);
	}

	return vertices;
}

//----------------------------------------------------------------------------------
// FillVertexDirectory - creates all vertices with the names in vertices, and adds 
// them to the static copy.
//----------------------------------------------------------------------------------
void VertexDirectory::FillVertexDirectory(set<vector<bool> >* vertices)
{
	//.. and add them to the VertexDirectory
	set<vector<bool> >::iterator it;
	for ( it=vertices->begin() ; it != vertices->end(); it++ )
    {
		Vertex* vertex;
		try
		{
			vertex = new Vertex(*it);
		}
		catch(std::bad_alloc *e) //memory allocation failed
		{	
			e = NULL;
			Logger::LogMessage("memory allocation failed in VertexDirectory::FillVertexDirectory. Vertex wasn't created.");
			throw new GraphColoringException("memory allocation failed in VertexDirectory::FillVertexDirectory. Vertex wasn't created.");
		}
		AddVertex(vertex);
	}	
}

//----------------------------------------------------------------------------------
// AddVertex
// adds the vertex to the static directory
//----------------------------------------------------------------------------------
void VertexDirectory::AddVertex(Vertex *vertex)
{
	VertexDirectory::AddVertexToDirectory(vertex, m_Vertices);
}

//----------------------------------------------------------------------------------
// AddWorkingCopyVertex
// adds a vertex to the non-static backup directory
//----------------------------------------------------------------------------------
void VertexDirectory::AddWorkingCopyVertex(Vertex *vertex)
{
	VertexDirectory::AddVertexToDirectory(vertex, m_WorkingCopyVertices);
}

//----------------------------------------------------------------------------------
// RemoveVertexFromDirectory
// removes the vertex from the given directory
//----------------------------------------------------------------------------------
void VertexDirectory::RemoveVertexFromDirectory(vector<bool> name,
												map<vector<bool>, Vertex*>* vertices)
{
	map<vector<bool>,Vertex*>::iterator it = vertices->find(name); // Find vertex by name
	if (!(it == vertices->end	()))
	{
		vertices->erase(it);
	}
}

//----------------------------------------------------------------------------------
// RemoveVertex
// removes a vertex from the non-static backup directory
//----------------------------------------------------------------------------------
void VertexDirectory::RemoveWorkingCopyVertex(std::vector<bool> name)
{
	VertexDirectory::RemoveVertexFromDirectory(name, m_WorkingCopyVertices);
}

int VertexDirectory::VertexCount()
{
	return VertexDirectory::m_Vertices->size();
}

void VertexDirectory::Clear()
{
	m_Vertices->clear();
}

map<vector<bool>, Vertex*>* VertexDirectory::m_Vertices = new map<vector<bool>, Vertex*>; // finds vertex by name

//----------------------------------------------------------------------------------
// GetStaticCopyVertices
// returns a map of all the vertices in the directory
//----------------------------------------------------------------------------------
map<vector<bool>, Vertex*>* VertexDirectory::GetStaticCopyVertices()
{
	return m_Vertices;
}

//----------------------------------------------------------------------------------
// GetWorkingCopyVertices
// returns a map of all the vertices in the backup directory
//----------------------------------------------------------------------------------
map<vector<bool>, Vertex*>* VertexDirectory::GetWorkingCopyVertices()
{
	return m_WorkingCopyVertices;
}


//----------------------------------------------------------------------------------
// GetVertexFromDirectory
// returns a pointer to the vertex from the right dir
// if dir == NULL from static directory
//----------------------------------------------------------------------------------
Vertex* VertexDirectory::GetVertexFromDirectory(VertexKey name, VertexDirectory *dir)
{
	Vertex *v;
	if (dir == NULL)
	{	//use static directory
		v = VertexDirectory::GetStaticCopyVertex(name);
	}
	else
	{	// use the directory associated with this vertex
		v = dir->GetWorkingCopyVertex(name);
	}
	if (v == NULL)
	{
		throw(new GraphColoringException("Vertex not in in directory"));
	}

	return v;
}


//----------------------------------------------------------------------------------
// UpdateVertexDirectoryFromOther
// copies all the vertex data from one directory to another
// Does not copy containers and creates new data structures (group, graph)
// These need to be cloned after using this function
//----------------------------------------------------------------------------------
void VertexDirectory::UpdateVertexDirectoryFromOther(VertexDirectory *updateFrom, VertexDirectory *updateTo)
{
	map<VertexKey, Vertex*>::iterator it;
	map<VertexKey, Vertex*> *vertices;

	// If we're updating the static copy, get the static vertices
	if (updateTo == NULL)
	{
		Debug::PrintDebugMsg("Updating static vertex directory");
		vertices = VertexDirectory::GetStaticCopyVertices();
	} else
	{
		// else, get the working copy ones from the directory we're updating
		Debug::PrintDebugMsg("Updating a working copy directory");
		vertices = updateTo->GetWorkingCopyVertices();
	}

	if (updateFrom == NULL)
	{
		Debug::PrintDebugMsg("Copying vertex data from static directory");
	} else
	{
		Debug::PrintDebugMsg("Copying vertex data from working directory");
	}

	// Go over all vertices and copy
	for (it = vertices->begin(); it != vertices->end(); it++)
	{
		Vertex *vertexToUpdate = it->second;
		vertexToUpdate->Copy(GetVertexFromDirectory(it->first,updateFrom));
	}

	Debug::PrintDebugMsg("Update complete");
}

//----------------------------------------------------------------------------------
// AddVertexSet: Adds a new vertexset to the list
//----------------------------------------------------------------------------------
void VertexDirectory::AddVertexSet(VertexSet *set)
{
	map<VertexSetKey, VertexSet*>::iterator it = m_VertexSets.find(set->Name());
	if (it == m_VertexSets.end())
	{
		pair<map<VertexSetKey, VertexSet*>::iterator, bool> res;
		res = m_VertexSets.insert(pair<VertexSetKey, VertexSet*>(set->Name(), set));
		if (res.second == false)
		{
			throw new GraphColoringException("Could not add vertexset to map");
		}
	} else
	{
		it->second = set;
	}
}

//----------------------------------------------------------------------------------
// GetVertexSet: Gets vertex set by name. Throws exception if doesn't exist
//----------------------------------------------------------------------------------
VertexSet *VertexDirectory::GetVertexSet(VertexSetKey name)
{
	map<VertexSetKey, VertexSet*>::iterator it = m_VertexSets.find(name);
	if (it == m_VertexSets.end())
	{
		throw new GraphColoringException("Vertex set not in directory");
	}

	return it->second;
}

//----------------------------------------------------------------------------------
// ClearAllVertexSets: clears the vertex set list
//----------------------------------------------------------------------------------
void VertexDirectory::ClearAllVertexSets()
{
	m_VertexSets.clear();
}

//----------------------------------------------------------------------------------
// GetSavedVertexSets: Returns a pointer to the vertex set list
//----------------------------------------------------------------------------------
map<VertexSetKey, VertexSet*> *VertexDirectory::GetSavedVertexSets()
{
	return &(m_VertexSets);
}

//-----------------------------------------------------------------------------------------
// Private
//-----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------
// AddVertexToDirectory
// adds the vertex to the given directory
//----------------------------------------------------------------------------------
void VertexDirectory::AddVertexToDirectory(Vertex *vertex, std::map<vector<bool>,Vertex*> *vertices)
{
	// Do not allow multiple inserts of same vertex
	if (vertices->find(vertex->Name())== vertices->end())
	{
		vertices->insert(pair<vector<bool>,Vertex*>(vertex->Name(), vertex));
	}
}

//----------------------------------------------------------------------------------
// RemoveVertex
// removes a vertex from the static directory
//----------------------------------------------------------------------------------
void VertexDirectory::RemoveVertex(vector<bool> name)
{
	VertexDirectory::RemoveVertexFromDirectory(name, m_Vertices);
}

//----------------------------------------------------------------------------------
// GetStaticCopyVertexFromDirectory
// returns a pointer to the vertex with the name "name" in the given directory
//----------------------------------------------------------------------------------
Vertex* VertexDirectory::GetStaticCopyVertexFromDirectory(vector<bool> name, 
												map<vector<bool>, Vertex*>* vertices)
{
	map<vector<bool>, Vertex*>* verticesDir;
	if (vertices == NULL)
	{	//use static directory
		verticesDir = m_Vertices;
	}
	else
	{	//use supplied directory
		verticesDir = vertices;
	}
	map<vector<bool>,Vertex*>::iterator it = verticesDir->find(name); // Find vertex by name
	if (it == verticesDir->end())
	{
		// vertex not found - should never happen
		string str = "Vertex not found: ";
		for(int i=0; i< (int)name.size(); i++)
		{
			str += name[i];
		}
		Debug::PrintDebugMsg(str);
		return NULL;
	}
	return it->second;
}

